package colite

import (
	"strings"

	"github.com/PuerkitoBio/goquery"
	"golang.org/x/net/html"
)

// HTMLElement HTML节点类
type HTMLElement struct {
	// Index 节点索引
	Index int

	// Name 节点名称
	Name string

	// Text 节点文本内容
	Text string

	// DOM 当前匹配节点
	DOM *goquery.Selection

	// Request 当前请求实例
	Request *Request

	// Response 当前响应实例
	Response *Response

	// 节点所有属性
	attributes []html.Attribute
}

// NewHTMLElementFromSelectionNode 从goquery.Selection节点创建HTMLElement实例
func NewHTMLElementFromSelectionNode(resp *Response, s *goquery.Selection, n *html.Node, idx int) *HTMLElement {
	return &HTMLElement{
		Index:      idx,
		Name:       n.Data,
		Text:       goquery.NewDocumentFromNode(n).Text(),
		DOM:        s,
		Request:    resp.Request,
		Response:   resp,
		attributes: n.Attr,
	}
}

// Attr 获取当前节点指定属性的值(如不存在返回空字符串)
func (h *HTMLElement) Attr(k string) string {
	for _, a := range h.attributes {
		if a.Key == k {
			return a.Val
		}
	}
	return ""
}

// ChildText 获取子节点中所有匹配标签的文本内容(组合在一起)
func (h *HTMLElement) ChildText(goquerySelector string) string {
	return strings.TrimSpace(h.DOM.Find(goquerySelector).Text())
}

// ChildTexts 获取子节点中所有匹配标签的文本内容(字符串数组)
func (h *HTMLElement) ChildTexts(goquerySelector string) []string {
	var res []string
	h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) {
		res = append(res, strings.TrimSpace(s.Text()))
	})
	return res
}

// ChildAttr 获取子节点中第一个匹配标签指定属性的值
func (h *HTMLElement) ChildAttr(goquerySelector, attrName string) string {
	if attr, ok := h.DOM.Find(goquerySelector).Attr(attrName); ok {
		return strings.TrimSpace(attr)
	}
	return ""
}

// ChildAttrs 获取子节点中所有匹配标签指定属性的值(字符串数组)
func (h *HTMLElement) ChildAttrs(goquerySelector, attrName string) []string {
	var res []string
	h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) {
		if attr, ok := s.Attr(attrName); ok {
			res = append(res, strings.TrimSpace(attr))
		}
	})
	return res
}

// ForEach 每个匹配标签执行指定回调函数
func (h *HTMLElement) ForEach(goquerySelector string, callback func(int, *HTMLElement)) {
	i := 0
	h.DOM.Find(goquerySelector).Each(func(_ int, s *goquery.Selection) {
		for _, n := range s.Nodes {
			callback(i, NewHTMLElementFromSelectionNode(h.Response, s, n, i))
			i++
		}
	})
}

// ForEachWithBreak 每个匹配标签执行指定回调函数(可终止)
func (h *HTMLElement) ForEachWithBreak(goquerySelector string, callback func(int, *HTMLElement) bool) {
	i := 0
	h.DOM.Find(goquerySelector).EachWithBreak(func(_ int, s *goquery.Selection) bool {
		for _, n := range s.Nodes {
			if callback(i, NewHTMLElementFromSelectionNode(h.Response, s, n, i)) {
				i++
				return true
			}
		}
		return false
	})
}
